﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;

namespace BorderClippingExtension
{
    public class ClippingExtension
    {
        #region Attached Properties
        #region ClippingElements
        /// <summary>
        /// Gets the collection of elements that are used to clip the owning element.
        /// The method never returns null. If collection has not been set the method does it here (sets the empty collection).
        /// </summary>
        /// <param name="obj">The owning element to clip</param>
        /// <returns>The list of elements that are used to clip the owning element</returns>
        public static FreezableCollection<ClippingElementContainer> GetClippingElements(DependencyObject obj)
        {
            var clippingElements = (FreezableCollection<ClippingElementContainer>)obj.GetValue(ClippingElementsProperty);
            if (clippingElements == null)
            {
                clippingElements = new FreezableCollection<ClippingElementContainer>();
                SetClippingElements(obj, clippingElements);
            }
            return clippingElements;
        }
        /// <summary>
        /// For the given object sets the collection of elements that will clip it.
        /// This is the main entry to the whole ClippingExtension. 
        /// When the list of elements is set the appropriate private properties are also set.
        /// </summary>
        /// <param name="obj">Object that is to be clipped</param>
        /// <param name="value">Collection of elements that will clip the object</param>
        public static void SetClippingElements(DependencyObject obj, FreezableCollection<ClippingElementContainer> value)
        {
            obj.SetValue(ClippingElementsProperty, value);
        }
        /// <summary>
        /// Attached property with special name. This is done intentionally to allow adding elements of the colleciton in XAML directly
        /// without specyfying the collection object itself.
        /// The property is not interited by childred object (for performance) - it is enough that only the given object is clipped.
        /// Its children will be clipped automatically by it.
        /// </summary>
        public static readonly DependencyProperty ClippingElementsProperty =
            DependencyProperty.RegisterAttached("ClippingElementsInternal", typeof(FreezableCollection<ClippingElementContainer>), typeof(ClippingExtension),
            new PropertyMetadata(null, OnClippingElementsChanged));
        #endregion ClippingElements

        #region ClippingDistance
        /// <summary>
        /// Gets the space between each clipping element and the element to be clipped. 
        /// In other words it will be used to clip more space than the clipping elements themselves.
        /// </summary>
        /// <param name="obj">Element that is to be clipped</param>
        /// <returns>Space between each clipping element and the element to be clipped</returns>
        public static double GetClippingDistance(DependencyObject obj)
        {
            return (double)obj.GetValue(ClippingDistanceProperty);
        }
        /// <summary>
        /// Gets the space between each clipping element and the element to be clipped. 
        /// In other words it will be used to clip more space than the clipping elements themselves.
        /// </summary>
        /// <param name="obj">Element that is to be clipped</param>
        /// <param name="value">The number of pixels virtually "enlarging" the clipping elements.</param>
        public static void SetClippingDistance(DependencyObject obj, double value)
        {
            obj.SetValue(ClippingDistanceProperty, value);
        }
        /// <summary>
        /// The space between each clipping element and the element to be clipped.
        /// In other words it will be used to clip more space than the clipping elements themselves.
        /// The property is inheritable so each child (at the moment only Border is supported) knows how big "enlargement" is required.
        /// </summary>
        public static readonly DependencyProperty ClippingDistanceProperty =
            DependencyProperty.RegisterAttached("ClippingDistance", typeof(double), typeof(ClippingExtension), new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.Inherits));
        #endregion ClippingDistance

        #region AdorningElements
        /// <summary>
        /// For the given object (only Border is supported a the moment) gets the collection of elements that will add an appropriate pieces of path aroud each of them.
        /// Added adorning paths together with the original Border should look like if Border itself is surrounding the elements.
        /// </summary>
        /// <param name="obj">The object (currently only Border is supported) for which the adoring paths are to be added</param>
        /// <returns>Collection of elements that will be used to create paths pieces</returns>
        private static FreezableCollection<ClippingElementContainer> GetAdorningElements(DependencyObject obj)
        {
            return (FreezableCollection<ClippingElementContainer>)obj.GetValue(AdorningElementsProperty);
        }
        /// <summary>
        /// For the given object (only Border is supported a the moment) sets the collection of elements that will add an appropriate pieces of path aroud each of them.
        /// Added adorning paths together with the original Border should look like if Border itself is surrounding the elements.
        /// </summary>
        /// <param name="obj">The object (currently only Border is supported) for which the adoring paths are to be added</param>
        /// <param name="value"></param>
        private static void SetAdorningElements(DependencyObject obj, FreezableCollection<ClippingElementContainer> value)
        {
            obj.SetValue(AdorningElementsProperty, value);
        }
        /// <summary>
        /// Private property storing the collection of elements that will be used to add appropriate pieces of paths to the given object (currently only Border is supported).
        /// The property is set internally when the ClippingElements property is attached.
        /// The property is inheritable, so all childrent (actually Borders) will have a chance to be decorated with appropriate adorder.
        /// </summary>
        private static readonly DependencyProperty AdorningElementsProperty =
            DependencyProperty.RegisterAttached("AdorningElements", typeof(FreezableCollection<ClippingElementContainer>), typeof(ClippingExtension),
            new FrameworkPropertyMetadata(new FreezableCollection<ClippingElementContainer>(), FrameworkPropertyMetadataOptions.Inherits, OnAdorningElementsChanged));
        #endregion AdorningElements

        #region CommonAncestorElementsToClip
        /// <summary>
        /// For a given common ancestor gets the list of associated elements that should be clipped.
        /// Common ancestor means the one that is an ancestor for the given element that is to be clipped and the associated clipping elements.
        /// Because it is possible that more that one element that is to be clipped has the same ancestor the type of this property has to be a colleciton.
        /// Commont ancestor is important because changes in its size possibly require the clipping to be recalculated as element to be clipped and its clipping elements may move relatively to each others.
        /// This property is set automatically when the ClippingElements is attached.
        /// </summary>
        /// <param name="obj">Object that is a common ancestor for the element to be clipped and its clipping elements</param>
        /// <returns>Elements for which the cllipping needs to be recalculated when the given object is resized</returns>
        private static IList<FrameworkElement> GetCommonAncestorElementsToClip(DependencyObject obj)
        {
            return (IList<FrameworkElement>)obj.GetValue(CommonAncestorElementsToClipProperty);
        }
        /// <summary>
        /// For a given common ancestor sets the list of associated elements that should be clipped.
        /// Common ancestor means the one that is an ancestor for the given element that is to be clipped and the associated clipping elements.
        /// Because it is possible that more that one element that is to be clipped has the same ancestor the type of this property has to be a colleciton.
        /// Commont ancestor is important because changes in its size possibly require the clipping to be recalculated as element to be clipped and its clipping elements may move relatively to each others.
        /// This property is set automatically when the ClippingElements is attached.
        /// </summary>
        /// <param name="obj">Object that is a common ancestor for the element to be clipped and its clipping elements</param>
        /// <param name="value">Elements fo which the clipping neets to be recalculated when the given object is resize</param>
        private static void SetCommonAncestorElementsToClip(DependencyObject obj, IList<FrameworkElement> value)
        {
            obj.SetValue(CommonAncestorElementsToClipProperty, value);
        }
        /// <summary>
        /// For a given common ancestor stores the list of associated elements that should be clipped.
        /// Common ancestor means the one that is an ancestor for the given element that is to be clipped and the associated clipping elements.
        /// Because it is possible that more that one element that is to be clipped has the same ancestor the type of this property has to be a colleciton.
        /// Commont ancestor is important because changes in its size possibly require the clipping to be recalculated as element to be clipped and its clipping elements may move relatively to each others.
        /// This property is set automatically when the ClippingElements is attached.
        /// </summary>
        private static readonly DependencyProperty CommonAncestorElementsToClipProperty =
            DependencyProperty.RegisterAttached("CommonAncestorElementsToClip", typeof(IList<FrameworkElement>), typeof(ClippingExtension));
        #endregion CommonAncestorElementsToClip

        #region CommonAncestorElementsToAdorn
        /// <summary>
        /// For a given common ancestor gets the list of associated elements that should be decorated with adorners (currently only Border is supported).
        /// Common ancestor means the one that is an ancestor for the given element that is to be decorated and the associated elements that will be used to create the adorning paths.
        /// Because it is possible that more that one element that is to be decorated has the same ancestor the type of this property has to be a colleciton.
        /// Commont ancestor is important because changes in its size possibly require the adorners to be recalculated as element to be decorated and its adorning elements may move relatively to each others.
        /// This property is set automatically when the AdorningElements is attached.
        /// </summary>
        /// <param name="obj">Object that is a common ancestor for the element to be decorated with adorners (currently only Border is supported) and its associated elements that will be used to create the adorning paths</param>
        /// <returns>Elements for which the adorners needs to be recalculated when the given object is resized</returns>
        private static IList<Border> GetCommonAncestorElementsToAdorn(DependencyObject obj)
        {
            return (IList<Border>)obj.GetValue(CommonAncestorElementsToAdornProperty);
        }
        /// <summary>
        /// For a given common ancestor sets the list of associated elements that should be decorated with adorners (currently only Border is supported).
        /// Common ancestor means the one that is an ancestor for the given element that is to be decorated and the associated elements that will be used to create the adorning paths.
        /// Because it is possible that more that one element that is to be decorated has the same ancestor the type of this property has to be a colleciton.
        /// Commont ancestor is important because changes in its size possibly require the adorners to be recalculated as element to be decorated and its adorning elements may move relatively to each others.
        /// This property is set automatically when the AdorningElements is attached.
        /// </summary>
        /// <param name="obj">Object that is a common ancestor for the element to be decorated with adorners (currently only Border is supported) and its associated elements that will be used to create the adorning paths</param>
        /// <param name="value">The list of elements (currently only Border is supported) that needs to recalculate their adorners when the given object resizes</param>
        private static void SetCommonAncestorElementsToAdorn(DependencyObject obj, IList<Border> value)
        {
            obj.SetValue(CommonAncestorElementsToAdornProperty, value);
        }
        /// <summary>
        /// For a given common ancestor stores the list of associated elements that should be decorated with adorners (currently only Border is supported).
        /// Common ancestor means the one that is an ancestor for the given element that is to be decorated and the associated elements that will be used to create the adorning paths.
        /// Because it is possible that more that one element that is to be decorated has the same ancestor the type of this property has to be a colleciton.
        /// Commont ancestor is important because changes in its size possibly require the adorners to be recalculated as element to be decorated and its adorning elements may move relatively to each others.
        /// This property is set automatically when the AdorningElements is attached.
        /// </summary>
        private static readonly DependencyProperty CommonAncestorElementsToAdornProperty =
            DependencyProperty.RegisterAttached("CommonAncestorElementsToAdorn", typeof(IList<Border>), typeof(ClippingExtension));
        #endregion CommonAncestorElementsToAdorn
        #endregion Attached Properties

        #region Clipping events
        /// <summary>
        /// When the clipping elements are attached to the element that is to be clipped it is necessary to:
        /// 1. Set those elements as adoring elements for the given object (and automatically its children as AdorningElements is inheritable property).
        /// 2. If the element is already loaded calculate the clipping.
        /// 3. React when the element is loaded (or reloaded - may happen when the resources are replaced)
        /// </summary>
        private static void OnClippingElementsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var elementToClip = d as FrameworkElement;
            if (elementToClip == null)
            {
                return;
            }
            SetAdorningElements(elementToClip, GetClippingElements(elementToClip));
            if (elementToClip.IsLoaded)
            {
                SetClipping(elementToClip);
            }
            WeakEventManager<FrameworkElement, RoutedEventArgs>.AddHandler(elementToClip, "Loaded", OnElementToClipLoaded);
        }
        /// <summary>
        /// When the element is loaded or reloaded (may happen when the resources are replaced) it has to:
        /// 1. Recalculate its clipping
        /// 2. React on resizing of the common ancestor (common for it and its clipping elements).
        /// </summary>
        private static void OnElementToClipLoaded(object sender, RoutedEventArgs e)
        {
            var elementToClip = sender as FrameworkElement;
            HandleCommonAncestorClipSizeChanged(elementToClip);
            SetClipping(elementToClip);
        }
        /// <summary>
        /// Calculates the common ancestor (for the given element and its corresponding clipping elements).
        /// Adds the given element to the list associated with the ancestor and makes sure it will react on its size chnges.
        /// </summary>
        /// <param name="elementToClip">Element that is to be clipped and react on the common ancestor's size change</param>
        private static void HandleCommonAncestorClipSizeChanged(FrameworkElement elementToClip)
        {
            var clippingElements = GetClippingElements(elementToClip);
            if (clippingElements == null)
            {
                return;
            }
            var commonAncessor = elementToClip;
            foreach (var container in clippingElements)
            {
                var tempCommon = elementToClip.FindCommonVisualAncestor(container.Element) as FrameworkElement;
                if (tempCommon != null && tempCommon.IsAncestorOf(commonAncessor))
                {
                    commonAncessor = tempCommon;
                }
            }
            var elementsToClip = GetCommonAncestorElementsToClip(commonAncessor);
            if (elementsToClip == null)
            {
                elementsToClip = new List<FrameworkElement>();
                SetCommonAncestorElementsToClip(commonAncessor, elementsToClip);
                WeakEventManager<FrameworkElement, SizeChangedEventArgs>.AddHandler(commonAncessor, "SizeChanged", OnCommonAncestorClipSizeChanged);
            }
            if (!elementsToClip.Contains(elementToClip))
            {
                elementsToClip.Add(elementToClip);
            }
        }
        /// <summary>
        /// If the common ancestor's size changes the corresponding elements need to recalculate their clipping.
        /// </summary>
        private static void OnCommonAncestorClipSizeChanged(object sender, SizeChangedEventArgs e)
        {
            var elementsToClip = GetCommonAncestorElementsToClip(sender as DependencyObject);
            if (elementsToClip != null)
            {
                foreach (var elementToClip in elementsToClip)
                {
                    SetClipping(elementToClip);
                }
            }
        }
        #endregion Clipping events

        #region Adorning events
        /// <summary>
        /// When the adorning elements are set for the given element (currently only Border is supported) that is to be decorated with adorners it is necessary to:
        /// 1. If the element is already loaded calculate the adorners.
        /// 2. React when the element is loaded (or reloaded - may happen when the resources are replaced)
        /// </summary>
        private static void OnAdorningElementsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var elementToAdorn = d as Border;
            if (elementToAdorn == null)
            {
                return;
            }
            if (elementToAdorn.IsLoaded)
            {
                SetAdorners(elementToAdorn);
            }
            WeakEventManager<FrameworkElement, RoutedEventArgs>.AddHandler(elementToAdorn, "Loaded", OnElementToAdornLoaded);
        }
        /// <summary>
        /// When the element is loaded or reloaded (may happen when the resources are replaced) it has to:
        /// 1. Recalculate its adorners
        /// 2. React on resizing of the common ancestor (common for it and its adorning elements).
        /// </summary>
        private static void OnElementToAdornLoaded(object sender, RoutedEventArgs e)
        {
            var elementToAdorn = sender as Border;
            HandleCommonAncestorAdornSizeChanged(elementToAdorn);
            SetAdorners(elementToAdorn);
        }
        /// <summary>
        /// Calculates the common ancestor (for the given element and its corresponding adorning elements).
        /// Adds the given element to the list associated with the ancestor and makes sure it will react on its size chnges.
        /// </summary>
        /// <param name="elementToAdorn">Element that is to be decorated with adorners and react on the common ancestor's size change</param>
        private static void HandleCommonAncestorAdornSizeChanged(Border elementToAdorn)
        {
            var adorningElements = GetAdorningElements(elementToAdorn);
            if (adorningElements == null)
            {
                return;
            }
            FrameworkElement commonAncessor = elementToAdorn;
            foreach (var container in adorningElements)
            {
                var tempCommon = elementToAdorn.FindCommonVisualAncestor(container.Element) as FrameworkElement;
                if (tempCommon != null && tempCommon.IsAncestorOf(commonAncessor))
                {
                    commonAncessor = tempCommon;
                }
            }
            var elementsToAdorn = GetCommonAncestorElementsToAdorn(commonAncessor);
            if (elementsToAdorn == null)
            {
                elementsToAdorn = new List<Border>();
                SetCommonAncestorElementsToAdorn(commonAncessor, elementsToAdorn);
                WeakEventManager<FrameworkElement, SizeChangedEventArgs>.AddHandler(commonAncessor, "SizeChanged", OnCommonAncestorAdornSizeChanged);
            }
            if (!elementsToAdorn.Contains(elementToAdorn))
            {
                elementsToAdorn.Add(elementToAdorn);
            }
        }
        /// <summary>
        /// If the common ancestor's size changes the corresponding elements need to recalculate their adorners.
        /// </summary>
        private static void OnCommonAncestorAdornSizeChanged(object sender, SizeChangedEventArgs e)
        {
            var elementsToAdorn = GetCommonAncestorElementsToAdorn(sender as DependencyObject);
            if (elementsToAdorn != null)
            {
                foreach (var elementToAdorn in elementsToAdorn)
                {
                    SetAdorners(elementToAdorn);
                }
            }
        }
        #endregion Adorning events

        /// <summary>
        /// Clips the given element by excluding from it the associated clipping elements.
        /// </summary>
        /// <param name="elementToClip">Element that should be clipped</param>
        private static void SetClipping(FrameworkElement elementToClip)
        {
            if (elementToClip == null)
            {
                return;
            }
            //Retrieve elements that will clip the elementToClip
            var clippingElements = GetClippingElements(elementToClip);

            //Original geometry outlining the element that is to be clipped
            var clipGeometry = CreateOutlinedGeometry(elementToClip);

            var clippingDistance = GetClippingDistance(elementToClip);

            foreach (var container in clippingElements)
            {
                //Geometry that will be excluded from the original geometry of the elementToClip
                var clippingVisual = FindObject(container.Element);
                var elementGeometry = CreateOutlinedGeometry(clippingVisual, clippingDistance);

                //Transform the geometry to move it to where it is placed on UI.
                var transform = new TransformGroup();
                if (elementGeometry.Transform != null)
                {
                    //Preserve the transform already existing. Extremely important in the case of Paths.
                    transform.Children.Add(elementGeometry.Transform);
                }
                transform.Children.Add(clippingVisual.TransformToVisual(elementToClip) as Transform);
                elementGeometry.Transform = transform;

                //Exclude the geometry from the elementToClip geometry
                clipGeometry = new CombinedGeometry(GeometryCombineMode.Exclude, clipGeometry, elementGeometry);
            }
            elementToClip.Clip = clipGeometry;
        }

        /// <summary>
        /// Creates the appropriate adorner with pieces of Paths to make each element (currently only Border is supported)
        /// look like it is surrounding the associated AdorningElements (which are the same as ClippingElements).
        /// </summary>
        /// <param name="elementToAdorn">Element (currently only Border is supported) for which the adorners should be added</param>
        private static void SetAdorners(Border elementToAdorn)
        {
            if (elementToAdorn == null)
            {
                return;
            }
            var elementAdorner = GetElementAdorner(elementToAdorn);
            if (elementAdorner == null)
            {
                return;
            }
            //Elements that need to be surrounded using adorner
            var adorningElements = GetAdorningElements(elementToAdorn);
            if (adorningElements == null)
            {
                return;
            }

            var elementToAdornBrush = GetBrush(elementToAdorn);
            var elementToAdornThickness = GetThickness(elementToAdorn);
            var borderGeometry = CreateOutlinedGeometry(elementToAdorn);
            var clippingDistance = GetClippingDistance(elementToAdorn);

            //Clear previously calculated adorner elements
            elementAdorner.ClearElements();
            foreach (var container in adorningElements)
            {
                //Create geometry around the element
                var adorningVisual = FindObject(container.Element);
                var elementGeometry = CreateOutlinedGeometry(adorningVisual, clippingDistance);

                //Transform the geometry to move it to where it is placed on UI.
                var transform = new TransformGroup();
                if (elementGeometry.Transform != null)
                {
                    //Make sure to preserve the already existing transform. Extremely important for Paths.
                    transform.Children.Add(elementGeometry.Transform);
                }
                transform.Children.Add(adorningVisual.TransformToVisual(elementToAdorn) as Transform);
                elementGeometry.Transform = transform;

                //Create geometry that is an intersection of the element to be adorned and adorming element.
                var adornerClippingGeometry = new CombinedGeometry(GeometryCombineMode.Intersect, elementGeometry, borderGeometry);
                //To deal with Paths for which the outlining PathGeometry contains both: goemetry bigger then the Path -
                //- the one we are interested in, and the one that is smaller than Path - this one has to be excluded.
                var excludedGeometry = CreateOutlinedGeometry(adorningVisual);
                excludedGeometry.Transform = transform;
                adornerClippingGeometry = new CombinedGeometry(GeometryCombineMode.Exclude, adornerClippingGeometry, excludedGeometry);

                //Create a path clipped accordingly, using the elementToAdorn border style (brush/stroke and thickness).
                //The stroke thickness is 2x wider because shapes draw half of the stroke in and half out of the shape.
                var adornerPath = new Path
                {
                    Data = elementGeometry,
                    Stroke = elementToAdornBrush,
                    StrokeThickness = 2.0 * elementToAdornThickness,
                    Fill = null,
                    Clip = adornerClippingGeometry,
                };
                elementAdorner.AddElement(adornerPath);
            }
            //This one is necessary in some resizing scenario. Otherwise the adorner may not redraw after update.
            elementAdorner.InvalidateMeasure();
        }

        /// <summary>
        /// Gets the geometry outlining the given visual element by the specified distance.
        /// For Path element it is really a PathGeometry following the shape of the visual.
        /// For other types it is a RectangleGeometry with corner radius calculated using a separate method CalculateRadius.
        /// </summary>
        /// <param name="visual">Element for which the outlining geometry is to be calculated</param>
        /// <param name="distance">Desired distance from the element to the outlining geometry</param>
        /// <returns>Geometry outlining the given visula element by the specified distance</returns>
        private static Geometry CreateOutlinedGeometry(Visual visual, double distance = 0.0)
        {
            var path = visual as Path;
            if (path != null)
            {
                return distance > 0.0
                    ? path.RenderedGeometry.GetWidenedPathGeometry(new Pen(null, distance)).GetOutlinedPathGeometry()
                    : path.RenderedGeometry.GetOutlinedPathGeometry();
            }

            var elementRect = VisualTreeHelper.GetDescendantBounds(visual);
            elementRect = new Rect(elementRect.X - distance, elementRect.Y - distance, elementRect.Width + 2 * distance, elementRect.Height + 2 * distance);
            var clippingRadius = distance + CalculateRadius(visual);

            return new RectangleGeometry(elementRect)
            {
                RadiusX = clippingRadius,
                RadiusY = clippingRadius
            };
        }

        /// <summary>
        /// Calcualte corner radius for elements. Used for Border, Rectangle and Ellipse.
        /// Calculations for Paths do not use this method.
        /// </summary>
        /// <param name="element">Element with rounded corner</param>
        /// <returns>Element's corner radius</returns>
        private static double CalculateRadius(DependencyObject element)
        {
            var border = element as Border;
            if (border != null)
            {
                return border.CornerRadius.TopLeft;
            }
            var rectangle = element as Rectangle;
            if (rectangle != null)
            {
                return rectangle.RadiusX;
            }
            var ellipse = element as Ellipse;
            if (ellipse != null)
            {
                return ellipse.Width;
            }
            return 0.0;
        }

        /// <summary>
        /// Gets border's brush of the given element. "Border" means realy Border or Stroke if the element is a Shape.
        /// </summary>
        /// <param name="element">Element which border's brush is to be found</param>
        /// <returns>Brush of the element's border</returns>
        private static Brush GetBrush(FrameworkElement element)
        {
            var border = element as Border;
            if (border != null)
            {
                return border.BorderBrush;
            }
            var rectangle = element as Shape;
            if (rectangle != null)
            {
                return rectangle.Stroke;
            }
            return null;
        }

        /// <summary>
        /// Gets border's thickness of the given element. "Border" means realy Border or Stroke if the element is a Shape.
        /// </summary>
        /// <param name="element">Element which border's thicknes is to be found</param>
        /// <returns>Thickness of the element's border</returns>
        private static double GetThickness(FrameworkElement element)
        {
            var border = element as Border;
            if (border != null)
            {
                return border.BorderThickness.Left;
            }
            var rectangle = element as Shape;
            if (rectangle != null)
            {
                return rectangle.StrokeThickness;
            }
            return 0.0;
        }

        /// <summary>
        /// Gets the adorner of type ElementAdorner associated with the given element.
        /// If such adorner does not exists the method creates one.
        /// </summary>
        /// <param name="adornedElement">Element for which the adorner is to be retrieved</param>
        /// <returns>Adorner of type ElementAdorner associated with the given element</returns>
        private static ElementAdorner GetElementAdorner(FrameworkElement adornedElement)
        {
            AdornerLayer borderAdornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
            if (borderAdornerLayer == null)
            {
                return null;
            }

            ElementAdorner borderAdorner = null;
            Adorner[] allAdorners = borderAdornerLayer.GetAdorners(adornedElement);
            if (allAdorners != null)
            {
                borderAdorner = (from adorner in allAdorners where adorner is ElementAdorner select adorner as ElementAdorner).SingleOrDefault();
            }
            if (borderAdorner == null)
            {
                borderAdorner = new ElementAdorner(adornedElement);
                borderAdornerLayer.Add(borderAdorner);
            }
            return borderAdorner;
        }

        /// <summary>
        /// Searches the object that belong to the given root's subtree (root and its children).
        /// The object have to be of one of the types: Border, Rectangle, Ellipse or Path.
        /// If no such object is found the root object is returned.
        /// </summary>
        /// <param name="root">Root element of the subtree where the search should occur</param>
        /// <returns>Element of type Border, Rectangle, Ellipse or Path if exist, otherwise the root itself</returns>
        private static Visual FindObject(DependencyObject root)
        {
            return FindObject(root, typeof(Border), typeof(Rectangle), typeof(Ellipse), typeof(Path)) as Visual;
        }

        /// <summary>
        /// Searches the object that belong to the given root's subtree (root and its children).
        /// The object have to be of one of the given desired types.
        /// If no such object is found the root object is returned.
        /// </summary>
        /// <param name="root">Root element of the subtree where the search should occur</param>
        /// <param name="desiredTypes">List of the desired types to look for</param>
        /// <returns>Element of one of the desired types if exist, otherwise the root itself</returns>
        private static DependencyObject FindObject(DependencyObject root, params Type[] desiredTypes)
        {
            if (root != null)
            {
                if (desiredTypes.FirstOrDefault(type => type.IsInstanceOfType(root)) != null)
                {
                    return root;
                }

                int childrenCount = VisualTreeHelper.GetChildrenCount(root);
                for (int i = 0; i < childrenCount; i++)
                {
                    var child = VisualTreeHelper.GetChild(root, i);
                    if (desiredTypes.FirstOrDefault(type => type.IsInstanceOfType(child)) != null)
                    {
                        return child;
                    }
                }
                for (int i = 0; i < childrenCount; i++)
                {
                    var child = FindObject(VisualTreeHelper.GetChild(root, i), desiredTypes);
                    if (child != null)
                    {
                        return child;
                    }
                }
            }
            return root;
        }
    }

    /// <summary>
    /// Container for the clipping elements. Required to be able to bind a list of clipping elements to the attached property in XAML.
    /// It also forces the container to be a Freezable (just DependencyObject crashes during the startup).
    /// </summary>
    public class ClippingElementContainer : Freezable
    {
        /// <summary>
        /// Bindable clipping element.
        /// </summary>
        public Visual Element
        {
            get { return (Visual)GetValue(ElementProperty); }
            set { SetValue(ElementProperty, value); }
        }
        /// <summary>
        /// Dependency property storing the clipping element.
        /// </summary>
        public static readonly DependencyProperty ElementProperty =
             DependencyProperty.Register("Element", typeof(Visual), typeof(ClippingElementContainer));

        /// <summary>
        /// This is not used, but required by Freezable.
        /// </summary>
        protected override Freezable CreateInstanceCore()
        {
            throw new NotImplementedException();
        }
    }

    /// <summary>
    /// Concrete implementation of the abstract Adorner. Allows to add UIElements to the adorner.
    /// </summary>
    internal class ElementAdorner : Adorner
    {
        public ElementAdorner(UIElement adornedElement) : base(adornedElement) { }

        //List of added elements. Required for removal and to correctly implement abstract methods.
        private readonly IList<UIElement> elements = new List<UIElement>();

        /// <summary>
        /// Adds a new element to the adorner and places it in the correct place.
        /// Actually this methid just stores the placement (offset).
        /// The real placement is up to the ArrangeOverride method.
        /// </summary>
        /// <param name="element">Element to be added to the adorner</param>
        /// <param name="offset">Where to place the element</param>
        public void AddElement(UIElement element)
        {
            this.elements.Add(element);
            AddVisualChild(element);
        }
        /// <summary>
        /// Removes all the elements from the adorner.
        /// </summary>
        public void ClearElements()
        {
            foreach (var element in this.elements)
            {
                RemoveVisualChild(element);
            }
            this.elements.Clear();
        }

        protected override int VisualChildrenCount
        {
            get { return this.elements.Count; }
        }
        protected override Visual GetVisualChild(int index)
        {
            return this.elements[index];
        }

        protected override Size MeasureOverride(Size constraint)
        {
            foreach (var element in this.elements)
            {
                element.Measure(constraint);
            }
            return constraint;
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (var element in this.elements)
            {
                element.Arrange(new Rect(new Point(0.0, 0.0), element.DesiredSize));
            }
            return finalSize;
        }
    }
}
